﻿'
' Boogaloo® - http://www.mindfly.com
' Copyright (c) 2008
' by Mindfly, Inc.
'
' Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
' documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
' the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
' to permit persons to whom the Software is furnished to do so, subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included in all copies or substantial portions 
' of the Software.
'
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
' INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
' DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
' SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
' SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
' WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
' THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Imports System.Xml
Imports System.Text
Imports System.Web
Imports System.Globalization

Namespace XmlRpc

    ''' <summary>
    ''' Class represents one value from xml-rpc request or response
    ''' </summary>
    Public Class XmlRpcValue
        Inherits XmlRpcBase

        Public Sub New(ByVal value As Object, ByVal type As ValueType)
            Me.Value = value
            Me.type = type
        End Sub

        ''' <summary>
        ''' Constructor XmlNode passed is /params/param/value in Xml-Rpc spec
        ''' </summary>
        Public Sub New(ByVal node As XmlNode)

            Select Case node.LocalName


                Case "i4", "int"
                    Me.Value = CType(node.InnerText, Integer)
                    Me.type = ValueType.int

                Case "string"
                    Me.Value = node.InnerText.Trim
                    Me.type = ValueType.str

                Case "double"
                    Me.Value = CType(node.InnerText, Double)
                    Me.type = ValueType.dbl

                Case "Base64"
                    Me.Value = node.InnerText
                    Me.type = ValueType.bit

                Case "boolean"
                    Me.Value = CType(node.InnerText, Boolean)
                    Me.type = ValueType.bool

                Case "dateTime.iso8601"
                    Dim tempDate As String = node.InnerText.Replace("-", "")
                    Me.Value = DateTime.ParseExact(tempDate, "yyyyMMdd'T'HH':'mm':'ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal)
                    Me.type = ValueType.datetime

                Case "array"
                    Dim valueList As New List(Of XmlRpcValue)
                    Dim paramVal As XmlNode = Nothing
                    For Each paramVal In node.SelectNodes("data/value")
                        Dim val As New XmlRpcValue(paramVal.FirstChild)
                        valueList.Add(val)
                    Next
                    Me.Value = valueList
                    Me.type = ValueType.array

                Case "struct"
                    Dim dict As New Dictionary(Of String, XmlRpcValue)
                    Dim member As XmlNode = Nothing
                    For Each member In node.SelectNodes("member")
                        Dim name As String = member.SelectSingleNode("name").InnerText
                        Dim val As New XmlRpcValue(member.SelectSingleNode("value").FirstChild)
                        dict.Add(name, val)
                    Next
                    Me.Value = dict
                    Me.type = ValueType.struct

                Case Else
                    Me.Value = node.InnerText.Trim
                    Me.type = ValueType.str

            End Select


        End Sub



        Public Function ToXmlString(Optional ByVal IncludeTypeElement As Boolean = True) As String
            Return XmlRpcValueToXmlString(Me, IncludeTypeElement)
        End Function

        ''' <summary>
        ''' Converts XmlRpcValue to Xml-Rpc response xml string
        ''' </summary>
        Private Function XmlRpcValueToXmlString(ByVal resp As XmlRpcValue, ByVal IncludeTypeElement As Boolean) As String

            Dim sb As New StringBuilder

            If IncludeTypeElement Or resp.type = ValueType.struct Or resp.type = ValueType.array Then
                Select Case resp.type
                    Case XmlRpcValue.ValueType.int
                        sb.Append(String.Format("<int>{0}</int>", resp.Value.ToString))
                    Case XmlRpcValue.ValueType.dbl
                        sb.Append(String.Format("<double>{0}</double>", resp.Value.ToString))
                    Case XmlRpcValue.ValueType.bit
                        sb.Append(String.Format("<Base64>{0}</Base64>", resp.Value.ToString))
                    Case XmlRpcValue.ValueType.bool
                        sb.Append(String.Format("<boolean>{0}</boolean>", resp.Value.ToString).ToLower)
                    Case XmlRpcValue.ValueType.datetime
                        sb.Append(String.Format("<dateTime.iso8601>{0}</dateTime.iso8601>", ConvertDatetoISO8601(resp.Value)))
                    Case XmlRpcValue.ValueType.array
                        Dim vals As List(Of XmlRpcValue) = CType(resp.Value, List(Of XmlRpcValue))
                        Dim v As XmlRpcValue = Nothing
                        sb.Append("<array><data>")
                        For Each v In vals
                            sb.Append(String.Format("<value>{0}</value>", XmlRpcValueToXmlString(v, IncludeTypeElement)))
                        Next
                        sb.Append("</data></array>")

                    Case XmlRpcValue.ValueType.struct
                        Dim dict As Dictionary(Of String, XmlRpcValue) = CType(resp.Value, Dictionary(Of String, XmlRpcValue))

                        sb.Append("<struct>")
                        Dim keypair As KeyValuePair(Of String, XmlRpcValue) = Nothing
                        For Each keypair In dict
                            sb.Append("<member>")
                            sb.Append(String.Format("<name>{0}</name>", HttpContext.Current.Server.HtmlEncode(keypair.Key)))
                            sb.Append(String.Format("<value>{0}</value>", XmlRpcValueToXmlString(keypair.Value, IncludeTypeElement)))
                            sb.Append("</member>")

                        Next
                        sb.Append("</struct>")

                    Case Else ' assume string
                        sb.Append(String.Format("<string>{0}</string>", HttpContext.Current.Server.HtmlEncode(resp.Value)))
                End Select
            Else
                sb.Append(HttpContext.Current.Server.HtmlEncode(resp.Value))
            End If

            Return sb.ToString

        End Function

        Private _value As Object
        Public Property Value() As Object
            Get
                Return _value
            End Get
            Set(ByVal value As Object)
                _value = value
            End Set
        End Property


        Private _type As ValueType
        Public Property type() As ValueType
            Get
                Return _type
            End Get
            Set(ByVal vt As ValueType)
                _type = vt
            End Set
        End Property

        Public Enum ValueType
            int
            str
            dbl
            bit
            bool
            datetime
            array
            struct
        End Enum

    End Class

End Namespace